#include "l2_sender.h"

static dbl_list_t *sender_window = NULL;

BOOL l2_sender_init()
{
    dbl_list_t *temp = NULL;
    sender_window_info_t *wnd_info = NULL;
    uint16_t i = 0;
    
    sender_window = create_dbl_list();
    if (sender_window == NULL) { return FALSE; }

    wnd_info = MEM_ALLOC(sizeof(sender_window_info_t));
    wnd_info->buffer_count = 0;

    for (i = 0; i < MAX_SEQ; i++)
    {
        temp = MEM_ALLOC(sizeof(dbl_list_t));
        if (temp == NULL) { return FALSE; }

        temp->node_data = (void *)NULL;

        temp->prev = sender_window;
        temp->next = sender_window->next;
        sender_window->next->prev = temp;
        sender_window->next = temp;

        wnd_info->pending_buffers[i] = FALSE;
    }


    sender_window->node_data = (void *)wnd_info;
    
    return TRUE;
}

static void handle_l2_ack(void *msg)
{
    uint16_t ack_seq_num = 0;
    l2_hdr_t *l2_hdr = GET_L2_HDR_FROM_FRAME(msg);
    sender_window_info_t *wnd_info = (sender_window_info_t *)sender_window->node_data;

    ack_seq_num = l2_hdr->ack_seq;
    if (ack_seq > MAX_SEQ || wnd_info->pending_buffers[ack_seq_num] == FALSE)
    {
        // @todo: trace an error
        return;
    }

    // Remove all pending buffers with seq-num before the ack-seq-num
    dbl_list_t *pending_node = sender_window->next;
    while (pending_node != sender_window)
    {
        sender_buffer_t *pending_buffer = (sender_buffer_t *)pending_node->node_data;
        uint16_t seq_num = pending_buffer->seq_no;

        // Remove the buffer
        pending_buffer->prev->next = pending_buffer->next;
        pending_buffer->next->prev = pending_buffer->prev;

        // Free node data
        FREE_L2_FRAME(pending_buffer->frame_ptr);
        MEM_FREE(pending_buffer);

        wnd_info->pending_buffers[seq_num] = FALSE;
        wnd_info->count--;
        if (seq_num == ack_seq_num)
        {
            break;
        }
    }

    // Append new buffers to the window (if available)
    while (wnd_info->buffer_count < (SENDER_WINDOW_SIZE) && l3_packet_queue->count)
    {
        dbl_list_t *next_packet_node = l3_packet_queue->next;

        wnd_info->last_seq_num = (wnd_info->last_seq_num + 1) % (MAX_SEQ_NUM + 1);
        void *frame_ptr = BUILD_L2_FRAME((void *)next_packet_node->node_data, wnd_info->last_seq_num);

        // remove packet from l3 queue
        l3_packet_queue->next->next->prev = l3_packet_queue;
        l3_packet_queue->next = l3_packet_queue->next-next;
        l3_packet_queue->count--;

        // Encapsulate frame in the pending node
        dbl_list_t *pending_node = MEM_ALLOC(sizeof(dbl_list_t));
        sender_buffer_t *sender_buffer = MEM_ALLOC(sizeof(sender_buffer_t));

        sender_buffer->seq_no = wnd_info->last_seq_num;
        sender_buffer->timer_handle = START_TIMER_FOR_PENDING_BUFFER(BUFFER_RESPONSE_TIMEOUT, sender_buffer->seq_no);
        sender_buffer->frame_ptr = (void *)frame_ptr;
        sender_buffer->frame_len = GET_L2_FRAME_LEN(frame_ptr);

        pending_node->node_data = (void *)sender_buffer;

        // Append pending node to the sliding window
        dbl_list_t *tail_node = sender_window->prev;
        pending_node->prev = tail_node;
        pending_node->next = sender_window;
        tail_node->next = pending_node;

        wnd_info->buffer_count++;
        wnd_info->pending_buffers[sender_buffer->seq_no] = TRUE;

        SEND_L2_FRAME(frame_ptr);
    }

    return;
}

static void handle_l2_nak(void *msg)
{
    uint16_t nack_seq_num = 0;
    l2_hdr_t *l2_hdr = GET_L2_HDR_FROM_FRAME(msg);
    sender_window_info_t *wnd_info = (sender_window_info_t *)sender_window->node_data;
    
#ifdef USE_NAK_BITMAP
#error: USE_NAK_BITMAP implementation not done
#endif
    
    ack_seq_num = l2_hdr->ack_seq;
    if (ack_seq > MAX_SEQ || wnd_info->pending_buffers[ack_seq_num] == FALSE)
    {
        // @todo: trace an error
        return;
    }

    dbl_list_t *pending_node = sender_window->next;
    while (pending_node != sender_window)
    {
	sender_buffer_t *sender_buffer = (sender_buffer_t *)pending_node->node_data;
        if (nack_seq_num == sender_buffer->ack_seq)
        {
	    //Found the buffer
	    SEND_L2_FRAME(sender_buffer->frame_ptr);
	    sender_buffer->timer_handle = RESTART_TIMER_FOR_PENDING_BUFFER(sender_buffer->timer_handle, \
		BUFFER_RESPONSE_TIMEOUT, sender_buffer->seq_no);
	    
    
    return;
}

static void handle_unknown_l2_message(void *msg)
{
    return;
}

static void handle_timer_expiry(void *msg)
{
    return;
}

static void handle_message_from_l3(void *msg)
{
    return;
}

static void handle_unknown_event(void *msg)
{
    return;
}

BOOL l2_sender_main(size_t sender_obj)
{
      BOOL ret_val = TRUE;

      ret_val = l2_sender_init();
      if (!ret_val)
      {
           return FALSE;
      }

      while (1)
      {
          void *msg = BLOCKING_WAIT_ON_QUEUE(sender_obj);

          if (IS_MSG_FROM_L1(msg))
          {
               if (IS_MSG_L2_ACK(msg))
               {
                   handle_l2_ack(msg);
               } else if (IS_MSG_L2_NAK(msg))
               {
                   handle_l2_nak(msg);
               } else
               {
                   handle_unknown_l2_message(msg);
               }
          } else if (IS_MSG_TIMER_EXPIRY(msg))
          {
              handle_timer_expiry(msg);

          } else if (IS_MSG_FROM_L3(msg))
          {
              handle_message_from_l3(msg);
          } else {
              handle_unknown_event(msg);
          }
          
       }

      // Function should not return
      return;
}


